package com.ruoyi.common.utils.file;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import com.ruoyi.common.config.TencentCosConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.Seq;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

/**
 * 腾讯云对象存储工具类 - 优化版，确保流正确关闭
 */
@Component
public class TencentCosUtils {
    private static final Logger log = LoggerFactory.getLogger(TencentCosUtils.class);
    private static TencentCosConfig cosConfig;

    @Autowired
    public void setCosConfig(TencentCosConfig cosConfig) {
        TencentCosUtils.cosConfig = cosConfig;
    }

    /**
     * 默认大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 获取 COS 客户端
     */
    private static COSClient getCosClient() {
        // 1 初始化用户身份信息(secretId, secretKey)
        COSCredentials cred = new BasicCOSCredentials(cosConfig.getSecretId(), cosConfig.getSecretKey());
        // 2 设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        Region region = new Region(cosConfig.getRegion());
        // 3 ClientConfig 中包含了后续请求 COS 的客户端设置
        ClientConfig clientConfig = new ClientConfig(region);
        // 设置请求协议为 HTTPS
        clientConfig.setHttpProtocol(HttpProtocol.https);
        // 4 生成 cos 客户端
        return new COSClient(cred, clientConfig);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 访问URL
     * @throws IOException
     */
    public static String upload(MultipartFile file) throws IOException {
        try {
            return upload(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 文件上传
     *
     * @param file              上传的文件
     * @param allowedExtension  上传文件类型
     * @return 返回上传成功的文件访问路径
     * @throws FileSizeLimitExceededException       如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException                          比如读写文件出错时
     * @throws InvalidExtensionException            文件校验异常
     */
    public static String upload(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException {
        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNamelength > DEFAULT_FILE_NAME_LENGTH) {
            throw new FileNameLengthLimitExceededException(DEFAULT_FILE_NAME_LENGTH);
        }

        assertAllowed(file, allowedExtension);

        String fileName = extractFilename(file);
        
        COSClient cosClient = null;
        InputStream inputStream = null;
        try {
            // 获取COS客户端
            cosClient = getCosClient();
            
            // 获取输入流
            inputStream = file.getInputStream();
            
            ObjectMetadata objectMetadata = new ObjectMetadata();
            // 设置 Content-Type
            objectMetadata.setContentType(getContentType(FilenameUtils.getExtension(file.getOriginalFilename())));
            // 设置文件大小
            objectMetadata.setContentLength(file.getSize());
            
            // 构建文件上传请求
            String key = getFileKey(fileName);
            PutObjectRequest putObjectRequest = new PutObjectRequest(cosConfig.getBucketName(), key, inputStream, objectMetadata);
            
            // 执行上传
            PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
            
            log.info("File uploaded successfully to COS: {}", key);
            
            // 返回访问URL
            return getFileUrl(key);
        } catch (Exception e) {
            log.error("Failed to upload file to COS", e);
            throw e;
        } finally {
            // 确保关闭流和客户端
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("Failed to close input stream", e);
                }
            }
            if (cosClient != null) {
                cosClient.shutdown();
            }
        }
    }

    /**
     * 获取文件ContentType
     */
    private static String getContentType(String extension) {
        if (StringUtils.isEmpty(extension)) {
            return "application/octet-stream";
        }
        extension = extension.toLowerCase();
        switch (extension) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "pdf":
                return "application/pdf";
            case "doc":
                return "application/msword";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls":
                return "application/vnd.ms-excel";
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "txt":
                return "text/plain";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 编码文件名
     */
    private static String extractFilename(MultipartFile file) {
        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
                FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    private static String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension)) {
            extension = MimeTypeUtils.getExtension(file.getContentType());
        }
        return extension;
    }

    /**
     * 校验文件大小和类型
     *
     * @param file 上传的文件
     * @param allowedExtension 允许的文件类型
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException 文件校验异常
     */
    private static void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException {
        long size = file.getSize();
        if (size > DEFAULT_MAX_SIZE) {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, fileName);
            } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, fileName);
            } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, fileName);
            } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, fileName);
            } else {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }
    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     */
    private static boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件在COS中的存储Key
     */
    private static String getFileKey(String fileName) {
        return StringUtils.isEmpty(cosConfig.getPrefix()) ? fileName : cosConfig.getPrefix() + "/" + fileName;
    }

    /**
     * 获取文件访问URL
     */
    private static String getFileUrl(String key) {
        // 使用腾讯云COS的域名构建完整URL
        if (cosConfig != null && StringUtils.isNotEmpty(cosConfig.getDomain())) {
            // 确保域名不以/结尾
            String domain = cosConfig.getDomain();
            if (domain.endsWith("/")) {
                domain = domain.substring(0, domain.length() - 1);
            }
            
            // 确保key不以/开头
            String fileKey = key;
            if (fileKey.startsWith("/")) {
                fileKey = fileKey.substring(1);
            }
            
            // 返回完整的COS URL
            return domain + "/" + fileKey;
        }
        
        // 如果配置缺失，则返回相对路径作为后备
        String relativePath = key;
        if (StringUtils.isNotEmpty(cosConfig.getPrefix())) {
            relativePath = key.substring(cosConfig.getPrefix().length() + 1); // +1 是为了去掉斜杠
        }
        
        return Constants.RESOURCE_PREFIX + "/" + relativePath;
    }
} 